package com.xctech.yace.manger;

import com.alibaba.fastjson.JSONObject;
import com.xctech.yace.enumeration.DimMapping;
import com.xctech.yace.factory.MybatisSessionFactory;
import com.xctech.yace.mapper.DimMapper;
import com.xctech.yace.pojo.RefreshCacheParameter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 维度表数据管理类
 * @author junfeng.chen@xuncetech.com
 * @date 2021/12/6
 */
@Slf4j
public class DimDataManager extends DataManager{


    /**
     * 对象实例
     */
    private static volatile DimDataManager instance;
    public volatile ArrayBlockingQueue<RefreshCacheParameter> queues =  new ArrayBlockingQueue(100);

    /**
     * 数据版本
     */
    public volatile long stamp = 0L;

    //Unsafes类实例
    private static final Unsafe unsafe = reflectGetUnsafe();

    //stampOffset是stamp在对象头中的偏移量
    private static long stampOffset = 0L;

    static {
        try {
            //获取stamp在对象头中的偏移量
            stampOffset = getFieldOffset(unsafe, DimDataManager.class, "stamp");
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }
    //DCL创建
    public static DimDataManager getInstance() {
        //第一次检测
        if (instance == null) {
            //同步
            synchronized (DimDataManager.class) {
                //第二次检测
                if (instance == null) {
                    instance = new DimDataManager();
                    // 一个TM 只能start一次
//                    new RefreshCacheThread().start();
                }
            }
        }
        return instance;
    }
    /**
     * 更新指定表数据
     */
    public void updateListMap(Long stamp, String tableName, String type, JSONObject value){
        /**
         * compareAndSwapStamp 中调用CAS的接口， 保证只有一个线程更新stamp成功
         * 无法更新的线程return
         */
        if (compareAndSwapStamp(stamp)) {
            RefreshCacheParameter refreshCacheParameter = new RefreshCacheParameter(tableName, type, value);
            try {
                queues.put(refreshCacheParameter);
            } catch (InterruptedException e) {
                log.error("放入队列refreshCacheParameter:{}，异常：{}", refreshCacheParameter,e.getMessage());
            }
        } else {
            log.warn("execute not update table ：{}", tableName);
        }
    }

    /**
     * 获取Unsafe实例
     * Unsafe提供loadFence、storeFence、fullFence等方法，
     * 这些方法是在Java 8新引入的，用于定义内存屏障，避免代码重排序，与Java内存模型相关
     *
     * @return
     */
    private static Unsafe reflectGetUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取字段的内存偏移量
     *
     * @param unsafe
     * @param clazz
     * @param fieldName
     * @return
     */
    private static long getFieldOffset(Unsafe unsafe, Class clazz, String fieldName) {
        try {
            return unsafe.objectFieldOffset(clazz.getDeclaredField(fieldName));
        } catch (NoSuchFieldException e) {
            throw new Error(e);
        }
    }

    /**
     * CAS修改stamp值
     *
     * @param version
     * @return
     */
    public boolean compareAndSwapStamp(long version) {
        boolean flag = false;
        if (version != stamp) {
            if (version > stamp) {
                if (unsafe.compareAndSwapLong(instance, stampOffset, stamp, version)) {
                    flag = true;
                }
            }
        }
        return flag;
    }

//    public static void main(String[] args) {
        //本地测试
//        Properties props = new Properties();
//        props.setProperty("driver", "oracle.jdbc.driver.OracleDriver");
//        props.setProperty("url", "jdbc:oracle:thin:@192.168.0.63:1521:orcl");
//        props.setProperty("username", "dc");
//        props.setProperty("password", "dc");
//        SqlSession sqlSession = MybatisSessionFactory.getSqlSessionFactory(props).openSession();
//        DimDataManager.getInstance().loadData(sqlSession);
//        HashMap<String, Object> tableData = DimDataManager.getInstance().getTableData(DimMapping.VAR_SCR_INFO.getTable());
//        HashMap<String, Object> tableData = DimDataManager.getInstance().getTableData(DimMapping.PTY_PD_BASE_INFO.getTable());
//        for (String s : tableData.keySet()) {
//            System.out.println(tableData.get(s));
//        }

//        System.out.println(tableData.size());
//        for (String s : tableData.keySet()) {
//            System.out.println(s);
//        }

//        Object record = DimDataManager.getInstance().getRecord(DimMapping.VAR_SCR_INFO.getTable(), "10200121019");
//        System.out.println(record == null);
//        Integer eval = TypeCd.eval("1000121019", 1);
//        System.out.println(eval);
//    }

    /**
     * mapper以及parametersMaps
     * @param sqlSession
     */
    @Override
    public void loadData(SqlSession sqlSession) {
        DimMapper mapper = sqlSession.getMapper(DimMapper.class);
        initData(mapper, null, false);
    }
}
